#ifndef __tenacitas_interlace_text_core__coordinates__h__
#define __tenacitas_interlace_text_core__coordinates__h__


/** @file */


/** @todo */

// ==> C++ Headers

// ==> 3rds Headers
#include <vector>

// ==> Our Headers
#include <tenacitas.return_code.core/rc.h>
#include "coordinate.h"

// ==> Namespaces
using namespace std;

// ==> Macro Constants

// ==> Macro Commands


/** ==> namespace of the organization */
namespace tenacitas {
    /** ==> namespace for the project */
    namespace interlace_text {
        /** ==> bussiness, core, displaying, interacting, transmitting,
            storing */ 
        namespace core {

            // ==> Typedefs 

            // ==> Pre-Declarations

            // ==> Attributes 

            // ==> Functions

            // ==> Classes

            /** 
               @tparam supplier must define @p text and @p interlace

               @tparam supplier::text must define @p text, which is the type of
               string to be used: @p std::string, or @p std::wstring, or @p
               std::u16string, or @p std::u32string.

               @tparam supplier::interlace is the algorithm used to interlace the
               texts 

               @nosubgrouping 
            */
            template <typename supplier>
            class coordinates {
            public:

                // ==> coordinates - Public Internal Types

                /** */
                typedef typename supplier::text text;

                /** */
                typedef typename text::value_type letter;

                /** */
                typedef typename core::coordinate<supplier> coordinate;
                
                /** */
                typedef typename std::vector<coordinate> collection;

                /** */
                typedef typename collection::size_type index;

                /** */
                enum constants : constant_id {index_overflow = 0,
                        index_underflow = 1};

                // ==> coordinates - Friends

                /** Class allowed to access private members of @p coordinates, in
                    order to perform tests */
                friend class coordinates_tester;

                /** */
                friend class  supplier::interlace;

                /** */
                friend std::ostream & operator << (std::ostream & p_out,
                                                   const coordinates & p_coo) {

                    for (size_t _i = 0; _i < p_coo.m_collection.size(); ++_i){
                        p_out << p_coo.m_collection[_i] << " ";
                    }
                    return p_out;
                }
                

                // ==> coordinates - Public Constructors

                /** */
                inline coordinates();

                /** */
                inline coordinates(const text &);

                /** */
                inline coordinates(const coordinates &);

                /** */
                inline coordinates(coordinates &&);

                /** Destructor */
                inline ~coordinates();

                // ==> coordinates - Public Accessors

                /** */
                inline index get_size() const;
                
                /** */
                inline bool is_positioned() const noexcept;

                /** */
                inline void unposition() noexcept;
                
                // /** */
                // text get_text() const ;

                /** */
                void add(const coordinate &);
                
                // ==> coordinates - Public Helpers

                // ==> coordinates - Public Processors

                // ==> coordinates - Public Operators

                /** */
                inline coordinates & operator=(const coordinates &) ;

                /** */
                inline coordinates & operator=(coordinates &&) ;

                /** */
                inline bool operator ==(const coordinates & ) noexcept;

                /** */
                inline bool operator !=(const coordinates & ) noexcept;

                /** 

                 @throw rc<>
                */
                inline coordinate & operator[](index p_index) ;

                /**  
                     @throw rc<>
                */
                inline const coordinate & operator[](index p_index) const;


                /** not allowed */
                void* operator new (std::size_t size)
                    throw (std::bad_alloc) = delete; 

                /** not allowed */
                void operator delete (void *p_class_id) = delete;

                /** not allowed */
                void *operator new[] (size_t) = delete;

                /** not allowed */
                void operator delete[] (void *) = delete;

                // ==> coordinates - Public Attributes

                static const class_id id = 14310893457801ULL;

            private:

                // ==> coordinates - Private Internal Types

                // ==> coordinates - Private Processors

                /** */
                void spread_text(const text & p_text);


                // ==> coordinates - Private Operators

                // ==> coordinates - Private Attributes

                /** */
                collection m_collection;


            };
        }
    }
}
// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::interlace_text::core;

// -----------------------------------------------------------------------------
template<typename supplier>
const class_id
coordinates<supplier>::
id;

// -----------------------------------------------------------------------------
template<typename supplier>
coordinates<supplier>::
coordinates() 
    : m_collection() {}

// -----------------------------------------------------------------------------
template<typename supplier>
coordinates<supplier>::
coordinates(const text & p_text) 
    : m_collection(static_cast<index>(p_text.size()), coordinate()) {

    spread_text(p_text);
}
                
// -----------------------------------------------------------------------------
template<typename supplier>
coordinates<supplier>::
coordinates(const coordinates & p_coords) 
    : m_collection(p_coords.m_collection) {}

// -----------------------------------------------------------------------------
template<typename supplier>
coordinates<supplier>::
coordinates(coordinates && p_coords) {
    m_collection = std::move(p_coords.m_collection);
}

// -----------------------------------------------------------------------------
template<typename supplier>
coordinates<supplier>::
~coordinates() {}
                
// -----------------------------------------------------------------------------
template<typename supplier>
typename coordinates<supplier>::index
coordinates<supplier>::
get_size() const {return m_collection.size();}
                
// -----------------------------------------------------------------------------
template<typename supplier>
bool
coordinates<supplier>::
is_positioned() const noexcept {
    index _last = m_collection.size();
    
    for (index _i = 0; _i < _last; ++_i) {
        if (!m_collection[_i].is_positioned()) {
            return false;
        }
    }
    return true;
}
                
// -----------------------------------------------------------------------------
template<typename supplier>
void
coordinates<supplier>::
unposition() noexcept {
    index _last = m_collection.size();
    index _i = 0;
    for (; _i < _last; ++_i) {    
        m_collection[_i].unposition();
    }
}

// -----------------------------------------------------------------------------
template<typename supplier>
void
coordinates<supplier>::
add(const coordinate & p_coord) {m_collection.push_back(p_coord);}

// -----------------------------------------------------------------------------
template<typename supplier>
coordinates<supplier> & 
coordinates<supplier>::
operator=(const coordinates & p_coords) {
    if (this != & p_coords) {
        m_collection = p_coords.m_collection;
    }
    return *this;
}

                
// -----------------------------------------------------------------------------
template<typename supplier>
coordinates<supplier> & 
coordinates<supplier>::
operator=(coordinates && p_coords) {
    if (this != & p_coords) {
        m_collection = std::move(p_coords.m_collection);
    }
    return *this;
}
                
// -----------------------------------------------------------------------------
template<typename supplier>
bool 
coordinates<supplier>::
operator ==(const coordinates & p_coords) noexcept {
    index _last = m_collection.size();
    index _i = 0;
    for (; _i < _last; ++_i) {    
        if (m_collection[_i] != p_coords.m_collection[_i]) {
            return false;
        }        
    }
    return true;
}

                
// -----------------------------------------------------------------------------
template<typename supplier>
bool
coordinates<supplier>::
operator !=(const coordinates & p_coords) noexcept {
    index _last = m_collection.size();
    index _i = 0;
    
    for (; _i < _last; ++_i) {    
        if (m_collection[_i] != p_coords.m_collection[_i]) {
            return true;
        }        
    }
    return false;
}
                
// -----------------------------------------------------------------------------
template<typename supplier>
typename coordinates<supplier>::coordinate &
coordinates<supplier>::
operator[](index p_index) {

    if (p_index >= m_collection.size()) {
        throw rc<>(id, constants::index_overflow, __FILE__, __LINE__);
    }

    if (p_index < 0) {
        throw rc<>(id, constants::index_underflow, __FILE__, __LINE__);
    }

    return m_collection[p_index];
}
                
// -----------------------------------------------------------------------------
template<typename supplier>
const typename coordinates<supplier>::coordinate &
coordinates<supplier>::
operator[](index p_index) const  {

    if (p_index >= m_collection.size()) {
        throw rc<>(id, constants::index_overflow, __FILE__, __LINE__);
    }

    if (p_index < 0) {
        throw rc<>(id, constants::index_underflow, __FILE__, __LINE__);
    }

    return m_collection[p_index];
}


// -----------------------------------------------------------------------------
template<typename supplier>
void
coordinates<supplier>::
spread_text(const text & p_text) {
    typename text::size_type _i = 0;
    typename text::size_type _last = p_text.size();
    for (; _i < _last; ++_i) {
        m_collection[static_cast<index>(_i)].set_letter(p_text[_i]);
    }
}

#endif
